package com.qinge.blog.utils.jwt;

import com.qinge.blog.utils.UUIDUtil;
import com.qinge.blog.utils.exception.ServiceException;
import io.jsonwebtoken.*;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;

/**
 * @author QDW
 * @date 2022/5/6 8:19
 * @description jwt 工具
 **/
public class JwtUtil {

    // 有效期为
    public static final Long JWT_TTL = 24* 60 * 60 *1000L;// 60 * 60 *1000  一个小时
    // 设置秘钥明文
    public static final String JWT_KEY = "qinge123456789";

    public static String getUUID(){
        return UUID.randomUUID().toString()
                .replace("-", "").toLowerCase();
    }

    /**
     * 生成jtw
     * @param subject token中要存放的数据（json格式）
     * @return
     */
    public static String createJWT(String subject) {
        JwtBuilder builder = getJwtBuilder(subject, JWT_TTL, getUUID());// 设置过期时间
        return builder.compact();
    }

    /**
     * 生成jtw
     * @param subject token中要存放的数据（json格式）
     * @param ttlMillis token超时时间
     * @return
     */
    public static String createJWT(String subject, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());// 设置过期时间
        return builder.compact();
    }

    private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        SecretKey secretKey = generalKey();
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        if(ttlMillis==null){
            ttlMillis=JwtUtil.JWT_TTL;
        }
        long expMillis = nowMillis + ttlMillis;
        Date expDate = new Date(expMillis);
        return Jwts.builder()
                .setId(uuid)              //唯一的ID
                .setSubject(subject)   // 主题  可以是JSON数据
                .setIssuer("sg")     // 签发者
                .setIssuedAt(now)      // 签发时间
                .signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥
                .setExpiration(expDate);
    }

    /**
     * 创建token
     * @param id
     * @param subject
     * @param ttlMillis
     * @return
     */
    public static String createJWT(String id, String subject, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);// 设置过期时间
        return builder.compact();
    }

    /**
     * 生成加密后的秘钥 secretKey
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

    /**
     * 解析
     *
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Claims parseJWT(String jwt) throws ServiceException {
        SecretKey secretKey = generalKey();
        Claims claims=null;
        try{
            claims=Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(jwt)
                    .getBody();
        }catch (ExpiredJwtException exception){
            claims=exception.getClaims();
        }
        return claims;
    }

    /**
     * @author QDW
     * @date 2022/5/7 11:32
     * @description 验证JWT
     * @param javaWebToken
     * @return boolean
     **/
    public static boolean  isSigned(String javaWebToken){
        SecretKey secretKey = generalKey();
        return Jwts.parser().setSigningKey(secretKey).isSigned(javaWebToken);
    }

    /**
     * @author QDW
     * @date 2022/5/7 11:32
     * @description 判断token是否时间过期
     * @param javaWebToken
     * @return boolean
     **/
    public static boolean  isExpired(String javaWebToken){
        SecretKey secretKey = generalKey();
        boolean flag=false;
        try{
           Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(javaWebToken)
                    .getBody();
        }catch (ExpiredJwtException exception){
            // 过期了
            flag=true;
        }
       return flag;
    }
}

